export interface ITrade {
  actions: number[];
  income: number;
}
type TTradeTrr = [ITrade?, ...ITrade[]];
type TMatrix = [TTradeTrr?, ...TTradeTrr[]];


export interface ITradeOnceResult {
  timeBuy: number;
  timeSell: number;
  income: number;
}

export const tradeOnceMaxIncome = (valueLine: number[]): ITradeOnceResult | 'no answer' => {
  if (valueLine.length > 0) {
    let timeBuy = 0;
    let timeSell = 0;
    let income = 0;

    let result: ITradeOnceResult = {
      timeBuy,
      timeSell,
      income
    };
  
    valueLine.forEach((value, index) => {
      const valueBuy = valueLine[timeBuy];
      if (value < valueBuy) {
        timeBuy = index;
        timeSell = index;
        income = 0;
      } else if (value > valueBuy) {
        const difference = value - valueBuy;
        if (difference >= income) {
          income = difference;
          timeSell = index;

          if (difference > result.income) {
            result.timeSell = index;
            result.income = difference;
            result.timeBuy = timeBuy;
          }
        }
      }
    });

    return result;
  }

  return 'no answer';
};

export const tradeInfinitelyMaxIncome = (valueLine: number[]): ITradeOnceResult[] | 'no answer' => {
  if (valueLine.length > 0) {
    const result: ITradeOnceResult[] = [];
    
    let timeBuy = 0;

    valueLine.forEach((valueSell, index) =>{
      const valueBuy = valueLine[timeBuy];
      
      if (valueSell > valueBuy) {
        result.push({
          timeBuy,
          timeSell: index,
          income: valueSell - valueBuy
        });
      }
      const lastResult = result[result.length - 1];
      const lastSecResult = result[result.length - 2];

      if (lastResult && lastSecResult && lastResult.timeBuy === lastSecResult.timeSell) {
        lastSecResult.timeSell = lastResult.timeSell;
        lastSecResult.income += lastResult.income;
        result.pop();
      }

      timeBuy = index;
    });

    return result;
  }

  return 'no answer';
};

export const tradeNTimesMaxIncome = (valueLine: number[], countTrace: number): ITrade | 'no answer' => {
  const isNotrade = (status: number) => status === 0;
  const isBuy = (status: number) => status !== 0 && status % 2 > 0;

  if (valueLine.length > 0) {
    const matrix: TMatrix = [];

    valueLine.forEach((value, index) => {
      const row: TTradeTrr = [];
      matrix.push(row);
      
      const preRow = matrix[index - 1]; 
      for (let c = 0; c < (countTrace * 2 + 1); c++) {
        if (isNotrade(c)) {
          row.push({
            actions: [index],
            income: 0
          });
        } else if (isBuy(c)) {
          if (preRow) {
            const preBuy = preRow[c] as ITrade;
            const preRowPreCol = preRow[c - 1] as ITrade;
            
            const val = Math.max(-value + preRowPreCol.income, preBuy.income);
            row.push({
              actions: preBuy.income > -value + preRowPreCol.income ? [...preBuy.actions] : [...preRowPreCol.actions, index],
              income: val
            });
          } else {
            row.push({
              actions: [index],
              income: -value,
            });
          }
        } else {
          if (preRow) {
            const preSell = preRow[c] as ITrade;
            const preRowPreCol = preRow[c - 1] as ITrade;

            const val = Math.max(value + preRowPreCol.income, preSell.income);
            row.push({
              actions: preSell.income > value + preRowPreCol.income ? [...preSell.actions] : [...preRowPreCol.actions, index],
              income: val
            });
          } else {
            row.push({
              actions: [index],
              income: value + (row[c - 1] as ITrade).income
            });
          }
        }
      }
    });

    return (matrix[valueLine.length - 1] as TTradeTrr)[countTrace*2] as ITrade;
  }

  return 'no answer';
};
